/*
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */

#ifndef BTREEINDEX_H
#define BTREEINDEX_H

#include "Bruinbase.h"
#include "PageFile.h"
#include "RecordFile.h"

//included by me
#include <iostream>

/**
 * The data structure to point to a particular entry at a b+tree leaf node.
 * An IndexCursor consists of pid (PageId of the leaf node) and 
 * eid (the location of the index entry inside the node).
 * IndexCursor is used for index lookup and traversal.
 */
typedef struct {
  // PageId of the index entry
  PageId  pid;  
  // The entry number inside the node
  int     eid;  
} IndexCursor;

//List of Nodes (...doubly linked?)
//Node: ptrs, value (i.e. Nodes)
	class Node {
	public:
		Node(PageId pid)
		{
			m_pid = pid;
			m_previous = NULL;
			m_next = NULL;
		}
		PageId retPid()
		{
			return m_pid;
		}
		void setNext(Node* next)
		{
			m_next = next;
		}
		Node* next()
		{
			return m_next;
		}
		void setPrev(Node* prev)
		{
			m_previous = prev;
		}
		Node* prev()
		{
			return m_previous;
		}
	private:
		PageId m_pid;
		Node* m_previous;
		Node* m_next;
	};

class NodeList{
public:
	NodeList(){
		m_head = NULL;
		m_tail = NULL;
		m_size = 0;
	}
	~NodeList(){
		Node* m_current = m_head;
		Node* m_curNext;
		while(m_current != NULL)
		{
			m_curNext = m_head->next();
			delete m_current;
			m_current = m_curNext;
		}
	}
	void deleteList(){
		Node* m_current = m_head;
		Node* m_curNext;
		while(m_current != NULL)
		{
			m_curNext = m_head->next();
			delete m_current;
			m_current = m_curNext;
		}
		m_head = NULL;
		m_tail = NULL;
		m_size = 0;
	}
	int insert(PageId pid){
		//empty list
		if(m_head == NULL)
		{
			m_head = new Node(pid);
			m_tail = m_head;
		}
		else
		{
			Node* tempNode = new Node(pid);
			tempNode->setPrev(m_tail);
			m_tail->setNext(tempNode);
			m_tail = tempNode;
		}
		return 0;
	}
	void printList(){
		Node* m_current = m_head;
		while (m_current != NULL)
		{
			std::cerr << m_current->retPid() << " ";
			m_current = m_current->next();
		}
	}
	//gets last item in list
	//also deletes last item in list
	//return -1 for empty list; 0 otherwise
	int getLast(PageId& pid)
	{
		if (m_tail == NULL)
			return -1;
		pid = m_tail->retPid();
		Node* temp = NULL;
		if(m_tail->prev() != NULL) {
			temp = m_tail->prev();
			temp->setNext(NULL);
		}
		delete m_tail;
		m_tail = temp;
		return 0;
	}
	//insert
private:
	Node* m_head;
	Node* m_tail;
	int m_size;
}; 

//PageId == -2 signals NULL value
//PageId == -1 contains treeHeight + rootPid


/**
 * Implements a B-Tree index for bruinbase.
 * 
 */
class BTreeIndex {
 public:
  BTreeIndex();

  /**
   * Open the index file in read or write mode.
   * Under 'w' mode, the index file should be created if it does not exist.
   * @param indexname[IN] the name of the index file
   * @param mode[IN] 'r' for read, 'w' for write
   * @return error code. 0 if no error
   */
  RC open(const std::string& indexname, char mode);

  /**
   * Close the index file.
   * @return error code. 0 if no error
   */
  RC close();
    
  /**
   * Insert (key, RecordId) pair to the index.
   * @param key[IN] the key for the value inserted into the index
   * @param rid[IN] the RecordId for the record being inserted into the index
   * @return error code. 0 if no error
   */
  RC insert(int key, const RecordId& rid);

  /**
   * Find the leaf-node index entry whose key value is larger than or
   * equal to searchKey and output its location (i.e., the page id of the node
   * and the entry number in the node) as "IndexCursor."
   * IndexCursor consists of pid (page id of the node that contains the 
   * searchKey) and eid (the entry number inside the node)
   * to indicate the location of a particular index entry in the B+tree.
   * Note that, for range queries, we need to scan the B+tree leaf nodes.
   * For example, if the query is "key > 1000", we should scan the leaf
   * nodes starting with the key value 1000. For this reason,
   * this function returns the location of the leaf node entry
   * for a given searchKey, instead of returning the RecordId
   * associated with the searchKey.
   * Using the returned "IndexCursor", you will have to call readForward()
   * to retrieve the actual (key, rid) pair from the index.
   * @param key[IN] the key to find
   * @param cursor[OUT] the cursor pointing to the first index entry
   * with the key value
   * @return error code. 0 if no error.
   */
  RC locate(int searchKey, IndexCursor& cursor);

  /**
   * Read the (key, rid) pair at the location specified by the index cursor,
   * and move foward the cursor to the next entry.
   * @param cursor[IN/OUT] the cursor pointing to an leaf-node index entry in the b+tree
   * @param key[OUT] the key stored at the index cursor location
   * @param rid[OUT] the RecordId stored at the index cursor location
   * @return error code. 0 if no error
   */
  RC readForward(IndexCursor& cursor, int& key, RecordId& rid);
  

 private:
  PageFile pf;         /// the PageFile used to store the actual b+tree in disk

  int      treeHeight; /// the height of the tree
  PageId   rootPid;    /// the PageId of the root node
  int min_key;
  int max_key;

  //added variables
  NodeList* m_listNodes;
  //not sure if I need this
  //PageId m_nextPid;
  char m_mode;

  /// Note that the content of the above two variables will be gone when
  /// this class is destructed. Make sure to store the values of the two 
  /// variables in disk, so that they can be reconstructed when the index
  /// is opened again later.
};

#endif /* BTREEINDEX_H */
